import clang.cindex
import os
import sys
import re
import json
import argparse
from pprint import pprint
from pathlib import Path

from utils import LLVM_LIBRARY_PATH, get_range_code_snippet, get_code_lines, is_system_function_declaration, clang_options, generate_header_path

def visit_node(node, symbol_table):
    if is_system_function_declaration(node):
        return
    
    # 提取宏定义
    if node.kind == clang.cindex.CursorKind.MACRO_DEFINITION:
        tokens = list(node.get_tokens())
        if not tokens:
            return

        if len(tokens) > 2:
            macro_name = node.spelling
            token_name = ""
            idx = 0
            for token in tokens:
                token_name += token.spelling
                idx += 1
                if token_name == node.spelling:
                    break
            start_token = tokens[idx]
            end_token = tokens[-1]
            include_header_code = get_code_lines(node.extent.start.file.name)
            start = start_token.extent.start
            end = end_token.extent.end
            value = get_range_code_snippet(include_header_code, start, end)
        elif len(tokens) == 2:
            value = tokens[1].spelling
        else:
            return
        if node.spelling == "NULL" and node.extent.start.line == 1 and node.extent.end.line == 1:
            return
        symbol_table[node.spelling] = {
            'category': 'macro',
            'value': value,
        }

    #elif node.kind == clang.cindex.CursorKind.INCLUSION_DIRECTIVE:
    #    tokens = list(node.get_tokens())
    
    # 提取枚举及其成员
    elif node.kind == clang.cindex.CursorKind.ENUM_DECL:
        enum_name = node.spelling or "anonymous_enum"
        for enum_member in node.get_children():
            if enum_member.kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL:
                member_name = enum_member.spelling
                if member_name in symbol_table and \
                        symbol_table[member_name]['category'] == 'enum_member' and \
                        symbol_table[member_name]['enum'] == "anonymous_enum":
                    symbol_table[member_name] = {
                        'category': 'enum_member',
                        'enum': enum_name
                    }

    # 提取结构体
    elif node.kind == clang.cindex.CursorKind.STRUCT_DECL:
        struct_members = {}
        for struct_member in node.get_children():
            if struct_member.kind == clang.cindex.CursorKind.FIELD_DECL:
                member_name = struct_member.spelling
                member_type = struct_member.type.spelling
                struct_members[member_name] = member_type
        if struct_members:
            symbol_table[node.spelling or "anonymous_struct"] = {
                'category': 'struct',
                'members': struct_members
            }

    # 提取联合体
    elif node.kind == clang.cindex.CursorKind.UNION_DECL:
        union_members = {}
        for union_member in node.get_children():
            if union_member.kind == clang.cindex.CursorKind.FIELD_DECL:
                member_name = union_member.spelling
                member_type = union_member.type.spelling
                union_members[member_name] = member_type

        if union_members:
            symbol_table[node.spelling or "anonymous_union"] = {
                'category': 'union',
                'members': union_members,
            }

    elif node.kind == clang.cindex.CursorKind.TYPEDEF_DECL:
        underlying_type = node.underlying_typedef_type.spelling
        symbol_table[node.spelling] = {
            'category': 'typedef',
            'original': underlying_type,
        }

        enum_members = []
        struct_union_members = {}
        idx = 0
        for member in node.get_children():
            if member.kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL:
                member_name = member.spelling
                if member_name:
                    symbol_table[member_name] = {
                        'category': 'enum_member',
                        'enum': node.spelling
                    }
                    enum_members.append(member_name)
            elif member.kind == clang.cindex.CursorKind.ENUM_DECL:
                enum_name = member.spelling or "anonymous_enum"
                for enum_member in member.get_children():
                    if enum_member.kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL:
                        member_name = enum_member.spelling
                        symbol_table[member_name] = {
                            'category': 'enum_member',
                            'enum': node.spelling, 
                        }
                        enum_members.append(member_name)

            elif member.kind == clang.cindex.CursorKind.FIELD_DECL:
                member_name = member.spelling
                member_type = member.type.spelling
                struct_union_members[member_name] = member_type

        if "enum " in underlying_type:
            symbol_table[node.spelling] = {
                'category': 'enum',
                'members': enum_members,
            }
        elif "struct " in underlying_type:
            symbol_table[node.spelling] = {
                'category': 'struct',
                'members': struct_union_members,
            }
        elif "union " in underlying_type:
            symbol_table[node.spelling] = {
                'category': 'union',
                'members': struct_union_members,
            }


    # 提取全局变量
    elif node.kind == clang.cindex.CursorKind.VAR_DECL:
        if node.linkage == clang.cindex.LinkageKind.EXTERNAL or \
                node.linkage == clang.cindex.LinkageKind.INTERNAL:  # 确保是全局变量
            var_type = node.type.spelling
            # 尝试获取变量的初始值
            init_value = None
            tokens = list(node.get_tokens())
            for i, token in enumerate(tokens):
                if token.spelling == "=":
                    var_value = ''.join(t.spelling for t in tokens[i+1:-1])
                    break
            #for child in node.get_children():
            #    if child.kind == clang.cindex.CursorKind.INTEGER_LITERAL:
            #        init_value = child.spelling
            #    elif child.kind == clang.cindex.CursorKind.FLOATING_LITERAL:
            #        init_value = child.spelling
            #    elif child.kind == clang.cindex.CursorKind.STRING_LITERAL:
            #        init_value = child.spelling
            #    elif child.kind == clang.cindex.CursorKind.CHARACTER_LITERAL:
            #        #init_value = child.literal
            #        init_value = child.spelling

            symbol_table[node.spelling] = {
                'category': 'global_var',
                'type': var_type,
                'init_value': init_value
            }

    # 提取函数声明
    elif node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
        #if not node.extent.start.file.name.endswith(".c"):
        #    return
        return_type = node.result_type.spelling
        params = [(param.spelling, param.type.spelling) for param in node.get_arguments()]
        symbol_table[node.spelling] = {
            'category': 'function',
            'return_type': return_type,
            'parameters': params
        }
     
    for child in node.get_children():
        if is_system_function_declaration(child):
            continue
        visit_node(child, symbol_table)

def construct_symbol_table(header_file, include_dirs):
    # 初始化clang库
    if not clang.cindex.Config.loaded:
        clang.cindex.Config.set_library_path(LLVM_LIBRARY_PATH)  # 修改为libclang路径

    # 创建索引
    index = clang.cindex.Index.create()

    # 解析header file
    translation_unit = index.parse(header_file, args=generate_header_path(include_dirs), options=clang_options)

    # 初始化符号表
    symbol_table = {}
    
    # 从根节点开始访问AST
    visit_node(translation_unit.cursor, symbol_table)

    return symbol_table

# if __name__ == "__main__":
#     # 创建 ArgumentParser 对象
#     parser = argparse.ArgumentParser(description="parse C file.")

#     # 添加命令行参数
#     parser.add_argument("--source_file", help="C文件的路径")

#     # 解析命令行参数
#     args = parser.parse_args()
#     c_file = args.source_file
#     from utils import system_header_paths
#     symbol_table = construct_symbol_table(c_file, system_header_paths)


def test_symbol_table():
    import sys
    
    # 设置测试源文件路径
    test_source_file = "test_files/src/test.c"
    
    # 模拟命令行参数
    sys.argv = [
        "c_symbol_table.py",
        "--source_file",
        test_source_file
    ]
    
    # 调用原main函数的逻辑
    parser = argparse.ArgumentParser(description="parse C file.")
    parser.add_argument("--source_file", help="C文件的路径")
    
    args = parser.parse_args()
    c_file = args.source_file
    from utils import system_header_paths
    symbol_table = construct_symbol_table(c_file, system_header_paths)
    
    # 打印符号表以查看结果
    pprint(symbol_table)

if __name__ == "__main__":
    test_symbol_table()
